package virtorina.server.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import virtorina.server.Base;

import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

public class Room {
	private final Integer id;
	private final String name;
	private Integer online;
	private LinkedList<Long> labels;
	private LinkedList<Integer> types;

	public Integer getID() {
		return id;
	}

	public Room(int id, String name, LinkedHashSet<Long> labels,
			LinkedList<Integer> types) {
		this.labels = new LinkedList<Long>(labels);
		this.online = 0;
		this.setTypes(types);
		this.id = id;
		this.name = name;
		try {
			Online.cleanRoon(id);
			CacheManager cm = CacheManager.getInstance();
			cm.registerCache(
					"Messages" + id,
					cm.getCacheFactory().createCache(
							Collections.<Integer, ChatMsg> emptyMap()));
			cm.registerCache("UsersLeave" + id, cm.getCacheFactory()
					.createCache(Collections.<Integer, Long> emptyMap()));
			cm.registerCache("UsersCome" + id, cm.getCacheFactory()
					.createCache(Collections.<Integer, Long> emptyMap()));
			cm.registerCache(
					"Users" + id,
					cm.getCacheFactory().createCache(
							Collections.<Long, User> emptyMap()));
			cm.registerCache(
					"Stuff" + id,
					cm.getCacheFactory().createCache(
							Collections.<String, Object> emptyMap()));
			System.out.println("register ok");
		} catch (CacheException e) {
			e.printStackTrace();
		}
		setStuffVal("name", name);
		setStuffVal("lastMessage", new Integer(0));
		setStuffVal("lastUsersLeave", new Integer(0));
		setStuffVal("lastUsersCome", new Integer(0));
		setStuffVal("idList", new LinkedHashSet<Long>());
		setStuffVal("ComplainedUsers",
				new HashMap<Long, LinkedHashSet<Integer>>());
		setStuffVal("Question", null);
		setStuffVal("nextTime", 0L);
		setStuffVal("status", false); // false - режим описания
										// вопроса, true - режим вопроса
		setStuffVal("info", null);
		setStuffVal("winners", null);
		setStuffVal("qtype", null);
		setStuffVal("online", 0);
	}

	private Object getStuffVal(String key) {
		Object o = CacheManager.getInstance().getCache("Stuff" + id)
				.get(key + id);
		// System.out.println("in id = " + id + " get <" + key + "><" + o +
		// ">");
		return o;
	}

	private void setStuffVal(String key, Object val) {
		// System.out.println("in id = " + id + " set <" + key + "><" + val +
		// ">");
		CacheManager.getInstance().getCache("Stuff" + id).put(key + id, val);
	}

	private Question getQuestion() {
		Question tq = (Question) getStuffVal("Question");
		if (tq == null) {
			setNewQuestion();
			return getQuestion();
		} else {
			return tq;
		}
	}

	private Cache getCache(String name) {
		name = name + id;
		Cache c = CacheManager.getInstance().getCache(name);
		return c;
	}

	public boolean addMessage(ChatMsg msg) {
		User u = (User) getCache("Users").get(msg.getUserID());
		msg.setUserName(u.getName());
		Integer lastMessage = (Integer) getStuffVal("lastMessage");
		getCache("Messages").put(lastMessage % 30, msg);
		lastMessage++;
		setStuffVal("lastMessage", lastMessage);
		return true;
	}

	public void leave(Long UserID) {
		getCache("Users").remove(UserID);
		Integer lastUsersLeave = (Integer) getStuffVal("lastUsersLeave") % 100;
		getCache("UsersLeave").put(lastUsersLeave, UserID);
		lastUsersLeave++;
		setStuffVal("lastUsersLeave", lastUsersLeave % 100);
		removeFromIdList(UserID);
	}

	void addToIdList(Long UserID) {
		LinkedHashSet<Long> ids = getIdList();
		ids.add(UserID);
		setStuffVal("idList", ids);
		Online.addUserOnline(UserID, this.id);
		setStuffVal("online", (Integer)getStuffVal("online")+1);
	}

	void removeFromIdList(Long UserID) {
		LinkedHashSet<Long> ids = getIdList();
		ids.remove(UserID);
		setStuffVal("idList", ids);
		Online.removeUserOnline(UserID, this.id);
		setStuffVal("online", (Integer)getStuffVal("online")-1);
	}

	LinkedHashSet<Long> getIdList() {
		LinkedHashSet<Long> ids = (LinkedHashSet<Long>) getStuffVal("idList");
		if (ids == null) {
			ids = Online.getidList(this.id);
		}
		return ids;
	}

	public boolean setNewUser(User user) {
		if (getCache("Users").containsKey(user.getUserID())) {
			return false;
		}

		Integer lastMessage = (Integer) getStuffVal("lastMessage");
		if (lastMessage < 5) {
			user.setLastMessage(0);
		} else {
			user.setLastMessage(lastMessage - 5);
		}
		user.setLastUsersCome(0);
		user.setLastUsersLeave(0);
		user.setNextTime(0);
		getCache("Users").put(user.getUserID(), user);
		Integer lastUserCome = (Integer) getStuffVal("lastUsersCome") % 100;
		getCache("UsersCome").put(lastUserCome, user.getUserID());
		setStuffVal("lastUsersCome", (lastUserCome + 1) % 100);
		addToIdList(user.getUserID());
		return true;
	}

	public void UpdateName(String name, Long id) {
		User u = getUser(id);
		u.setName(name);
		getCache("Users").put(u.getUserID(), u);
	}

	public int getLastMessage() {
		return (Integer) getStuffVal("lastMessage");
	}

	public Integer getLastUsersCome() {
		return (Integer) getStuffVal("lastUsersCome");
	}

	public Integer getLastUsersLeave() {
		return (Integer) getStuffVal("lastUsersLeave");
	}

	private long getAnonID() {
		Long id = (Long) Base.Stuff.get("anonymousID");
		Long rslt = id;
		Base.Stuff.put("anonymousID", --id);
		return rslt;
	}

	public Long setAnonym() {
		User anonym = new User();
		Long aID = (Long) getAnonID();
		anonym.setUserID(aID);
		anonym.setName("Anonymous");
		setNewUser(anonym);
		aID--;
		return anonym.getUserID();
	}

	public List<ChatMsg> getMessages(Long userID) {
		User u = getUser(userID);
		int last = u.getLastMessage();
		List<ChatMsg> msgs = new LinkedList<ChatMsg>();
		int StuffLast = (Integer) getStuffVal("lastMessage");
		for (int i = last; i % 30 != StuffLast % 30; i++) {
			try {
				ChatMsg cm = (ChatMsg) getCache("Messages").get(i % 30);
				if (cm.getRoomID().equals(id)) {
					msgs.add(cm);
				}
			} catch (ClassCastException e) {
				u.setLastMessage(StuffLast);
				getCache("Users").put(u.getUserID(), u);
				return msgs;
			}
		}
		u.setLastMessage(StuffLast);
		getCache("Users").put(u.getUserID(), u);
		return msgs;
	}

	public LinkedList<Long> getleavers(Long userID) {
		User u = getUser(userID);
		int last = u.getLastUsersLeave();
		LinkedList<Long> leavers = new LinkedList<Long>();
		int StuffLast = (Integer) getStuffVal("lastUsersLeave");
		a: for (int i = last; i % 100 != StuffLast % 100; i++) {
			try {
				leavers.add((Long) getCache("UsersLeave").get(i % 100));
			} catch (ClassCastException e) {
				break a;
			}
		}
		u.setLastUsersLeave((Integer) getStuffVal("lastUsersLeave"));
		getCache("Users").put(u.getUserID(), u);
		return leavers;
	}

	public List<User> getComes(Long userID) {
		System.out.println("room = " + id + " users = "
				+ getCache("Users").toString());
		User u = getUser(userID);
		int last = u.getLastUsersCome();
		List<User> comes = new LinkedList<User>();
		if (last == 0) {
			LinkedHashSet<Long> ids = getIdList();
			Iterator<Long> i = ids.iterator();
			while (i.hasNext()) {
				Long id = i.next();
				System.out.println("id here = " + id);
				comes.add((User) getCache("Users").get(id));
			}
			u.setLastUsersCome((Integer) getStuffVal("lastUsersCome"));
			u.setLastUsersLeave((Integer) getStuffVal("lastUsersLeave"));
			getCache("Users").put(u.getUserID(), u);
			return comes;
		}
		int StuffLast = (Integer) getStuffVal("lastUsersCome");
		Long t;
		User comer;
		a: for (int i = last; i % 100 != StuffLast % 100; i++) {
			try {
				t = (Long) getCache("UsersCome").get(i % 100);
			} catch (ClassCastException e) {
				break a;
			}
			if (getCache("Users").containsKey(t)) {
				comer = (User) getCache("Users").get(t);
				if (comer != null) {
					comes.add(comer);
				}
			}
		}
		u.setLastUsersCome((Integer) getStuffVal("lastUsersCome"));
		getCache("Users").put(u.getUserID(), u);
		return comes;
	}

	public User getUser(Long UserID) {
		if (getCache("Users").containsKey(UserID)) {
			return (User) getCache("Users").get(UserID);
		} else {
			return null;
		}
	}

	private void setNewQuestion() {
		Long last = null;
		if (getStuffVal("Question") != null) {
			System.out.println("qq");
			last = getQuestion().getID();
			updComplaints();
		}
		Question tq = Question.getRandomQuestion(labels, types, last);
		tq.setAuthorName(User.getUser(tq.getAuthor()).getName());
		long t = new Date().getTime() + tq.getDuration();
		tq.setTime(t);
		setStuffVal("Question", tq);
		setStuffVal("answer", tq.getAnswer());
		setStuffVal("nextTime", t);
		setStuffVal("winners", new LinkedList<Long>());
		setStuffVal("status", true);
		setStuffVal("qtype", tq.getType());
	}

	public Info getInfo(Long UserID) {
		User u = getUser(UserID);
		u.setNextTime(nextTime());
		getCache("Users").put(UserID, u);
		return (Info) getStuffVal("info");
	}

	private void updComplaints() {
		int plus = 0;
		int minus = 0;
		HashMap<Long, LinkedHashSet<Integer>> users = (HashMap<Long, LinkedHashSet<Integer>>) (getStuffVal("ComplainedUsers"));
		if (!users.isEmpty()) {
			Long qID = getQuestion().getID();
			LinkedList<Complaint> rslt = new LinkedList<Complaint>();
			Complaint c;
			for (int i = 0; i < 5; i++) {
				rslt.add(i, new Complaint(i, qID));
			}
			for (Entry<Long, LinkedHashSet<Integer>> e : users.entrySet()) {
				if (e.getValue().contains(-1)) {
					plus++;
					continue;
				} else {
					minus++;
				}
				for (Integer i : e.getValue()) {
					c = rslt.get(i);
					c.setUserID(e.getKey());
					rslt.set(i, c);
				}
			}
			Complaint.saveALL(rslt);
			setStuffVal("ComplainedUsers",
					new HashMap<Long, LinkedHashSet<Integer>>());
		}
		Question q = getQuestion();
		if (plus + minus != 0) {
			q.setNumOfRank(plus + minus + q.getNumOfRank());
			double t = plus - minus;
			t /= q.getNumOfRank();
			q.setRank(q.getRank() + t);
			if (q.getNumOfComplaint() == null) {
				q.setNumOfComplaint(minus);
			} else {
				q.setNumOfComplaint(q.getNumOfComplaint() + minus);
			}
		}
		Question.updQuestion(q);
	}

	private void setInfo() {
		// @TODO запись данных о победителях должна обязательно быть здесь -
		// чтобы лишний раз не ворошить Стафф
		LinkedList<Long> winners = (LinkedList<Long>) getStuffVal("winners");
		LinkedList<String> nicks = new LinkedList<String>();
		User u = null;
		int anons = 0;

		for (Long user : winners) {
			if (user < 0) {
				anons++;
			} else {
				u = this.getUser(user);
				nicks.add(u.getName());
			}
		}

		if (anons == 1) {
			nicks.add("Anonymous");
		} else if (anons > 0) {
			nicks.add("Anonymous x" + anons);
		}
		try {
			long duration = 10000L;
			Long t = new Date().getTime() + duration;
			setStuffVal("nextTime", t);
			Info info = new Info(getQuestion(), nicks, t);
			info.setDuration(duration);
			setStuffVal("info", info);
			setStuffVal("status", false);
		} catch (Exception e) {
			System.out.println("ошибка в сет_инфо!");
		}
	}

	private Long nextTime() {
		return (Long) getStuffVal("nextTime");
	}

	private boolean isQuestion() {
		if ((Boolean) getStuffVal("status")) {
			return true;
		} else {
			return false;
		}
	}

	private void chktime() {
		if (nextTime() <= new Date().getTime()) {
			if (isQuestion()) {
				setInfo();
			} else {
				setNewQuestion();
			}
		}
	}

	public int getNews(Long UserID) {
		chktime();
		User u = getUser(UserID);
		if (u == null) {
			return 0;
		}
		if (u.getNextTime() != nextTime()) {
			if (isQuestion()) {
				return 4;
			} else {
				return 5;
			}
		}
		if (u.getLastMessage() != getLastMessage()) {
			return 1;
		}
		if (u.getLastUsersCome() != getLastUsersCome()) {
			return 2;
		}
		if (u.getLastUsersLeave() != getLastUsersLeave()) {
			return 3;
		}
		return 0;
	}

	public Question getQuestion(Long UserID) {
		User u = getUser(UserID);
		u.setComplaint(false);
		u.setNextTime(nextTime());
		getCache("Users").put(UserID, u);
		return getQuestion();
	}

	public void refreshUser(User user) {
		Integer lastMessage = (Integer) getStuffVal("lastMessage");
		if (lastMessage < 5) {
			user.setLastMessage(0);
		} else {
			user.setLastMessage(lastMessage - 5);
		}
		user.setLastUsersCome(0);
		user.setLastUsersLeave(0);
		user.setNextTime(0);
		getCache("Users").put(user.getUserID(), user);
	}

	public int ChkAnsw(String answer, Long userID) {
		if (!isQuestion()) {
			return -1;
		}
		int type = (Integer) getStuffVal("qtype");
		LinkedList<String> answ = (LinkedList<String>) (getStuffVal("answer"));
		for (String s : answ) {
			if (type != 3) {
				if (s.equalsIgnoreCase(answer)) {
					LinkedList<Long> winners = (LinkedList<Long>) getStuffVal("winners");
					if (!winners.contains(userID)) {
						winners.add(userID);
						setStuffVal("winners", winners);
					}
					return 1;
				}
			} else if (type == 3) {
				try {
					String clat = answer.substring(0, answer.indexOf(','));
					String clng = answer.substring(answer.indexOf(',') + 1);
					clat.replace('.', ',');
					clng.replace('.', ',');
					Double dclat = new Double(clat);
					Double dclng = new Double(clng);
					String slat = s.substring(0, s.indexOf(','));
					String slng = s.substring(s.indexOf(',') + 1);
					Double dslat = new Double(slat);
					Double dslng = new Double(slng);
					if (Math.abs(dclat - dslat) < 5
							&& Math.abs(dclng - dslng) < 5) {
						LinkedList<Long> winners = (LinkedList<Long>) getStuffVal("winners");
						if (!winners.contains(userID)) {
							winners.add(userID);
							setStuffVal("winners", winners);
						}
						return 1;
					}
				} catch (Exception e) {
					return 0;
				}
			}
		}
		return 0;
	}

	public int setComplaint(Long UserID, int type) {
		if (!isQuestion()) {
			HashMap<Long, LinkedHashSet<Integer>> users = (HashMap<Long, LinkedHashSet<Integer>>) (getStuffVal("ComplainedUsers"));
			if (users.containsKey(UserID)) {
				LinkedHashSet<Integer> hs = users.get(UserID);
				if (!hs.contains(type)) {
					hs.add(type);
					users.put(UserID, hs);
					setStuffVal("ComplainedUsers", users);
					return 1;
				} else {
					return 0;
				}
			} else {
				LinkedHashSet<Integer> hs = new LinkedHashSet<Integer>();
				hs.add(type);
				users.put(UserID, hs);
				setStuffVal("ComplainedUsers", users);
				return 1;
			}
		} else {
			return 0;
		}
	}

	public String getName() {
		return name;
	}

	public LinkedList<Long> getLabels() {
		return labels;
	}

	public void setLabels(LinkedList<Long> labels) {
		this.labels = labels;
	}

	public LinkedList<Integer> getTypes() {
		return types;
	}

	public void setTypes(LinkedList<Integer> types) {
		this.types = types;
	}

	public Integer getOnline() {
		return (Integer)getStuffVal("online");
	}
}

class Pair<T, V> {
	T first;
	V second;
}
